
/*
 * Audio communication
 * Slaver 
 * MSB first little-endian
 * author: lxl 
 * data: 2013-01-27
 *
 */

#include "dev.h"
#include "config.h"
#include "ictl.h"
#include "iodev.h"
#include "arch.h"
#include "circbuf.h"
#include "ioctl.h"
#include "kernel.h"
#include "string.h"


#define AUDIO_TXIO_CHANNEL0	97
#define AUDIO_TXIO_CHANNEL1	101
#define AUDIO_TXIO_CHANNEL2	89

#define AUDIO_RXIO_CHANNEL0	102
#define AUDIO_TXIO_CHANNELL_NR	3
#define AUDIO_RXIO_CHANNELL_NR	1

#define START_BIT_LEVEL		(0)
#define STOP_BIT_LEVEL		(1)

#define SEND_START_BIT(io)	gpio_output(io, 0)
#define SEND_DATA_BIT(io, level)	gpio_output(io, level)
#define SEND_PARITY_BIT(io, level)	gpio_output(io, level)
#define SEND_STOP_BIT(io)	gpio_output(io, 1)

#define BAUD_DELAY_TIME	102




typedef struct {
	audio_config_t *config;
	uint32_t txio_channel[AUDIO_TXIO_CHANNELL_NR]; /* Audio tx channel selection */
}audio_gpio_inf_t;

#define LEVEL_WIDTH_US(baud)	((1000000/baud) - 2)



static audio_config_t config_audio = {
	.tx_channel = 0,
	.baud = 9600,
	.level_width = LEVEL_WIDTH_US(9600),
};

audio_gpio_inf_t *audio_inf, audio = {
	.config = &config_audio,
	.txio_channel = {AUDIO_TXIO_CHANNEL0, AUDIO_TXIO_CHANNEL1, AUDIO_TXIO_CHANNEL1},
};


static uint8_t manchester_encode_half_byte(uint8_t half_byte)
{
	uint8_t byte = 0, i;

	for (i = 0; i < 4; i ++) {
		
		byte <<= 1;
		if (half_byte & 0x8) {
			byte |= 0x1;
			byte <<= 1;
			byte &= 0xfe;
		}
		else  {
			byte &= 0xfe;
			byte <<= 1;
			byte |= 0x1;
		}
		half_byte <<= 1;
	}
	return byte;
}


static void send_byte(uint8_t data)
{
	uint8_t i = 8;
	uint32_t txio = audio_inf->txio_channel[audio_inf->config->tx_channel];
	
	while (i--) {
		SEND_DATA_BIT(txio, data & 0x80);
		data <<= 1;
		udelay(BAUD_DELAY_TIME);
	}

		
}

static uint8_t even_check(uint8_t byte)
{
	uint8_t i = 8, parity = 0;

	while (i --) {
		if (byte & 0x1) {
			parity ^= 0x1;
		}
		byte >>= 1;
	}
	return parity;
}
static void audio_sysnc(uint8_t count)
{
	int i;
	uint32_t txio = audio_inf->txio_channel[audio_inf->config->tx_channel];
		
	for (i = 0; i < count; i ++) {
		SEND_DATA_BIT(txio, 0);
		udelay(BAUD_DELAY_TIME);
		SEND_DATA_BIT(txio, 1);
		udelay(BAUD_DELAY_TIME);
	}
}

static ssize_t t1_audio_write(iodev_t *dev, const void *buffer, size_t size)
{
	uint8_t *pbuf = (uint8_t *)buffer;
	int len = size;
	uint32_t txio = audio_inf->txio_channel[audio_inf->config->tx_channel];
	int  parity = 0;
	
	__disable_interrupt();
	audio_sysnc(55);
	while (len --) {
		parity = even_check(*pbuf);
		send_byte(manchester_encode_half_byte((*pbuf >> 4) & 0x0f));
		send_byte(manchester_encode_half_byte(*pbuf & 0x0f));
		pbuf ++;
		SEND_PARITY_BIT(txio, parity);
		udelay(BAUD_DELAY_TIME);
		SEND_PARITY_BIT(txio, !parity);
		udelay(BAUD_DELAY_TIME);
	}
	audio_sysnc(10);
	__enable_interrupt();
	return size;
}	
	
	
static int t1_audio_init(iodev_t *dev)
{
	uint8_t i, nr;
	
	audio_inf = &audio;
	nr = sizeof(audio_inf->txio_channel) /sizeof(audio_inf->txio_channel[0]);
	for (i = 0; i < nr; i ++) {
		gpio_set_output(audio_inf->txio_channel[i], 0);
		gpio_output(audio_inf->txio_channel[i], 1);
	}
	return 0;
	
}

static int t1_audio_ioctl(iodev_t *dev, uint32_t cmd, void *arg)
{
	switch (cmd) {
	case IOCTL_AUDIO_PARAM: 
		{
			memcpy(audio_inf->config, (audio_config_t *)arg, sizeof(audio_config_t));
		}
		break;
	default:
		return -1;
		break;
	}
	return 0;
}

static const iodev_t t1_audio__ __iodev_entry = {
    .name = "audio",
    .priv = NULL,
    .init = t1_audio_init,
    .read = NULL,
    .write = t1_audio_write,
    .ioctl = t1_audio_ioctl,
    .poll = NULL,
};


